package org.gd.rjdbc.context;

import com.google.common.base.Preconditions;
import org.gd.rjdbc.annotation.TableField;
import org.gd.rjdbc.exceptions.RJDBCException;
import org.gd.rjdbc.interfaces.RJDBCListener;
import org.gd.rjdbc.interfaces.RSQListenerFactory;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
public abstract class RJDBCContext {

    private static volatile RSQListenerFactory instance = null;

    public synchronized static RSQListenerFactory getInstance() {
        if(instance == null) {
            synchronized (RSQListenerFactory.class) {
                if(instance == null) {
                    instance = getNewFactory();
                }
            }
        }
        return instance;
    }

    private static RSQListenerFactory getNewFactory() {
        return new RSQListenerFactory() {
            @Override
            public RJDBCListener getRSListener() {
                return new RJDBCListener() {
                    @Override
                    public <T> T parseOne(ResultSet rs, Class<T> clazz)
                            throws RJDBCException {
                        try {
                            Preconditions.checkNotNull(clazz, "class is null");
                            Preconditions.checkNotNull(rs, "ResultSet is null");
                            Object newInstance = clazz.newInstance();
                            for (Field field: clazz.getDeclaredFields()) {
                                if(field.isAnnotationPresent(TableField.class)) {
                                    TableField ann = field.getAnnotation(TableField.class);
                                    field.setAccessible(true);
                                    RJDBCParser.parseSqlElement(rs, field, newInstance, ann);
                                    field.setAccessible(false);
                                }
                            }
                            return ((T) newInstance);
                        } catch (Exception e) {
                            throw new RJDBCException(e);
                        }
                    }
                    @Override
                    public <T> List<T> parseAll(ResultSet rs, Class<T> clazz)
                            throws RJDBCException {
                        try {
                            Preconditions.checkNotNull(clazz, "class is null");
                            Preconditions.checkNotNull(rs, "ResultSet is null");
                            List<T> array = new ArrayList<T>();
                            while (rs.next()) {
                                Object newInstance = clazz.newInstance();
                                for (Field field: clazz.getDeclaredFields()) {
                                    if(field.isAnnotationPresent(TableField.class)) {
                                        TableField ann = field.getAnnotation(TableField.class);
                                        field.setAccessible(true);
                                        RJDBCParser.parseSqlElement(rs, field, newInstance, ann);
                                        field.setAccessible(false);
                                    }
                                }
                                array.add((T) newInstance);
                            }
                            return array;
                        } catch (Exception e) {
                            throw new RJDBCException(e);
                        }
                    }
                };
            }
        };
    }
}
